package lib4app;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import socket.DataField;
import socket.ProtocolMessage;
import struct.AppInfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DBController {
    private Log logger = LogFactory.getLog(DBController.class);
    private static DBController instance;
    AppRemoteConnector connector;

    public static DBController getInstance() {
        if (instance == null) {
            synchronized (DBController.class) {
                if (instance == null) {
                    instance = new DBController();
                }
            }
        }
        return instance;
    }

    private DBController() {
        connector = AppRemoteConnector.getInstance();
    }

    public boolean createTable(String tableName, String primaryKey, List<String> header, int rowLimit) {
        JSONObject jo = new JSONObject(5);
        jo.put(DataField.API_FIELD, ProtocolMessage.DB_CREATE);
        jo.put(AppInfo.APP_NAME_FIELD, connector.getAppName());
        jo.put(DataField.TABLE_NAME_FIELD, tableName);
        jo.put(DataField.PRIM_KEY_FIELD, primaryKey);
        jo.put(DataField.ROW_LIMIT_FIELD, rowLimit);
        JSONArray headerJson = new JSONArray();
        headerJson.addAll(header);
        jo.put(DataField.HEADER_FIELD, headerJson);
        connector.getTCP().send(jo.toJSONString());

        boolean state = false;
        String recv = connector.getTCP().recv();
        if (recv != null) {
            state = JSON.parseObject(recv).getBooleanValue(DataField.STATE_FIELD);
        }
        logger.info(String.format("[%s]: createTable(%s, %s, %s) -> %s", connector.getAppName(), tableName, primaryKey, header, state));
        return state;
    }

    public boolean deleteTable(String tableName) {
        JSONObject jo = new JSONObject(3);
        jo.put(DataField.API_FIELD, ProtocolMessage.DB_DROP);
        jo.put(AppInfo.APP_NAME_FIELD, connector.getAppName());
        jo.put(DataField.TABLE_NAME_FIELD, tableName);
        connector.getTCP().send(jo.toJSONString());

        boolean state = false;
        String recv = connector.getTCP().recv();
        if (recv != null) {
            state = JSON.parseObject(recv).getBooleanValue(DataField.STATE_FIELD);
        }
        logger.info(String.format("[%s]: deleteTable(%s) -> %s", connector.getAppName(), tableName, state));
        return state;
    }

    public boolean insertRow(String tableName, Map<String, Object> row) {
        JSONObject jo = new JSONObject(4);
        jo.put(DataField.API_FIELD, ProtocolMessage.DB_INSERT);
        jo.put(AppInfo.APP_NAME_FIELD, connector.getAppName());
        jo.put(DataField.TABLE_NAME_FIELD, tableName);
        JSONObject rowJson = new JSONObject();
        rowJson.putAll(row);
        jo.put(DataField.ROW_FIELD, rowJson);
        connector.getTCP().send(jo.toJSONString());

        boolean state = false;
        String recv = connector.getTCP().recv();
        if (recv != null) {
            state = JSON.parseObject(recv).getBooleanValue(DataField.STATE_FIELD);
        }
        logger.info(String.format("[%s]: insertRow(%s, %s) -> %s", connector.getAppName(), tableName, row, state));
        return state;
    }

    public List<Map<String, Object>> getAllRows(String tableName) {
        JSONObject jo = new JSONObject(3);
        jo.put(DataField.API_FIELD, ProtocolMessage.DB_ROWS);
        jo.put(AppInfo.APP_NAME_FIELD, connector.getAppName());
        jo.put(DataField.TABLE_NAME_FIELD, tableName);
        jo.put(DataField.ROWS_FIELD, -1);
        connector.getTCP().send(jo.toJSONString());

        List<Map<String, Object>> ret = new ArrayList<>();
        String recv = connector.getTCP().recv();
        if (recv != null) {
            JSON.parseArray(recv).forEach(obj -> {
                Map<String, Object> row = new HashMap<>((JSONObject) obj);
                ret.add(row);
            });
        }
        logger.info(String.format("[%s]: getAllRows(%s) -> %s", connector.getAppName(), tableName, ret));
        return ret;
    }
}
